From 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b Mon Sep 17 00:00:00 2001
From: Fuwn <50817549+Fuwn@users.noreply.github.com>
Date: Sat, 24 Jan 2026 13:09:50 +0000
Subject: Initial commit
Created from https://vercel.com/new
---
.../[websiteId]/cohorts/CohortAddButton.tsx | 21 ++++
.../[websiteId]/cohorts/CohortDeleteButton.tsx | 60 +++++++++
.../[websiteId]/cohorts/CohortEditButton.tsx | 37 ++++++
.../[websiteId]/cohorts/CohortEditForm.tsx | 135 +++++++++++++++++++++
.../[websiteId]/cohorts/CohortsDataTable.tsx | 24 ++++
.../websites/[websiteId]/cohorts/CohortsPage.tsx | 16 +++
.../websites/[websiteId]/cohorts/CohortsTable.tsx | 41 +++++++
.../(main)/websites/[websiteId]/cohorts/page.tsx | 12 ++
8 files changed, 346 insertions(+)
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx
create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/page.tsx
(limited to 'src/app/(main)/websites/[websiteId]/cohorts')
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx
new file mode 100644
index 0000000..3f7f872
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx
@@ -0,0 +1,21 @@
+import { useMessages } from '@/components/hooks';
+import { Plus } from '@/components/icons';
+import { DialogButton } from '@/components/input/DialogButton';
+import { CohortEditForm } from './CohortEditForm';
+
+export function CohortAddButton({ websiteId }: { websiteId: string }) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+ }
+ label={formatMessage(labels.cohort)}
+ variant="primary"
+ width="800px"
+ >
+ {({ close }) => {
+ return ;
+ }}
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx
new file mode 100644
index 0000000..94d62ff
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx
@@ -0,0 +1,60 @@
+import { ConfirmationForm } from '@/components/common/ConfirmationForm';
+import { useDeleteQuery, useMessages } from '@/components/hooks';
+import { Trash } from '@/components/icons';
+import { DialogButton } from '@/components/input/DialogButton';
+import { messages } from '@/components/messages';
+
+export function CohortDeleteButton({
+ cohortId,
+ websiteId,
+ name,
+ onSave,
+}: {
+ cohortId: string;
+ websiteId: string;
+ name: string;
+ onSave?: () => void;
+}) {
+ const { formatMessage, labels, FormattedMessage } = useMessages();
+ const { mutateAsync, isPending, error, touch } = useDeleteQuery(
+ `/websites/${websiteId}/segments/${cohortId}`,
+ );
+
+ const handleConfirm = async (close: () => void) => {
+ await mutateAsync(null, {
+ onSuccess: () => {
+ touch('cohorts');
+ onSave?.();
+ close();
+ },
+ });
+ };
+
+ return (
+ }
+ variant="quiet"
+ title={formatMessage(labels.confirm)}
+ width="400px"
+ >
+ {({ close }) => (
+ {name},
+ }}
+ />
+ }
+ isLoading={isPending}
+ error={error}
+ onConfirm={handleConfirm.bind(null, close)}
+ onClose={close}
+ buttonLabel={formatMessage(labels.delete)}
+ buttonVariant="danger"
+ />
+ )}
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx
new file mode 100644
index 0000000..0799071
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx
@@ -0,0 +1,37 @@
+import { CohortEditForm } from '@/app/(main)/websites/[websiteId]/cohorts/CohortEditForm';
+import { useMessages } from '@/components/hooks';
+import { Edit } from '@/components/icons';
+import { DialogButton } from '@/components/input/DialogButton';
+import type { Filter } from '@/lib/types';
+
+export function CohortEditButton({
+ cohortId,
+ websiteId,
+ filters,
+}: {
+ cohortId: string;
+ websiteId: string;
+ filters: Filter[];
+}) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+ }
+ variant="quiet"
+ title={formatMessage(labels.cohort)}
+ width="800px"
+ >
+ {({ close }) => {
+ return (
+
+ );
+ }}
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx
new file mode 100644
index 0000000..c755035
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx
@@ -0,0 +1,135 @@
+import {
+ Button,
+ Column,
+ Form,
+ FormButtons,
+ FormField,
+ FormSubmitButton,
+ Grid,
+ Label,
+ Loading,
+ TextField,
+} from '@umami/react-zen';
+import { useMessages, useUpdateQuery, useWebsiteCohortQuery } from '@/components/hooks';
+import { ActionSelect } from '@/components/input/ActionSelect';
+import { DateFilter } from '@/components/input/DateFilter';
+import { FieldFilters } from '@/components/input/FieldFilters';
+import { LookupField } from '@/components/input/LookupField';
+
+export function CohortEditForm({
+ cohortId,
+ websiteId,
+ filters = [],
+ onSave,
+ onClose,
+}: {
+ cohortId?: string;
+ websiteId: string;
+ filters?: any[];
+ showFilters?: boolean;
+ onSave?: () => void;
+ onClose?: () => void;
+}) {
+ const { data } = useWebsiteCohortQuery(websiteId, cohortId);
+ const { formatMessage, labels, messages, getErrorMessage } = useMessages();
+
+ const { mutateAsync, error, isPending, touch, toast } = useUpdateQuery(
+ `/websites/${websiteId}/segments${cohortId ? `/${cohortId}` : ''}`,
+ {
+ type: 'cohort',
+ },
+ );
+
+ const handleSubmit = async (formData: any) => {
+ await mutateAsync(formData, {
+ onSuccess: async () => {
+ toast(formatMessage(messages.saved));
+ touch('cohorts');
+ onSave?.();
+ onClose?.();
+ },
+ });
+ };
+
+ if (cohortId && !data) {
+ return ;
+ }
+
+ const defaultValues = {
+ parameters: { filters, dateRange: '30day', action: { type: 'path', value: '' } },
+ };
+
+ return (
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx
new file mode 100644
index 0000000..6734384
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx
@@ -0,0 +1,24 @@
+import { DataGrid } from '@/components/common/DataGrid';
+import { useWebsiteCohortsQuery } from '@/components/hooks';
+import { CohortAddButton } from './CohortAddButton';
+import { CohortsTable } from './CohortsTable';
+
+export function CohortsDataTable({ websiteId }: { websiteId?: string }) {
+ const query = useWebsiteCohortsQuery(websiteId, { type: 'cohort' });
+
+ const renderActions = () => {
+ return ;
+ };
+
+ return (
+
+ {({ data }) => }
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx
new file mode 100644
index 0000000..14f366e
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx
@@ -0,0 +1,16 @@
+'use client';
+import { Column } from '@umami/react-zen';
+import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
+import { Panel } from '@/components/common/Panel';
+import { CohortsDataTable } from './CohortsDataTable';
+
+export function CohortsPage({ websiteId }) {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx
new file mode 100644
index 0000000..5c7ac03
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx
@@ -0,0 +1,41 @@
+import { DataColumn, DataTable, type DataTableProps, Row } from '@umami/react-zen';
+import Link from 'next/link';
+import { CohortDeleteButton } from '@/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton';
+import { CohortEditButton } from '@/app/(main)/websites/[websiteId]/cohorts/CohortEditButton';
+import { DateDistance } from '@/components/common/DateDistance';
+import { useMessages, useNavigation } from '@/components/hooks';
+import { filtersObjectToArray } from '@/lib/params';
+
+export function CohortsTable(props: DataTableProps) {
+ const { formatMessage, labels } = useMessages();
+ const { websiteId, renderUrl } = useNavigation();
+
+ return (
+
+
+ {(row: any) => (
+ {row.name}
+ )}
+
+
+ {(row: any) => }
+
+
+ {(row: any) => {
+ const { id, name, parameters } = row;
+
+ return (
+
+
+
+
+ );
+ }}
+
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/cohorts/page.tsx b/src/app/(main)/websites/[websiteId]/cohorts/page.tsx
new file mode 100644
index 0000000..9946f60
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/cohorts/page.tsx
@@ -0,0 +1,12 @@
+import type { Metadata } from 'next';
+import { CohortsPage } from './CohortsPage';
+
+export default async function ({ params }: { params: Promise<{ websiteId: string }> }) {
+ const { websiteId } = await params;
+
+ return ;
+}
+
+export const metadata: Metadata = {
+ title: 'Cohorts',
+};
--
cgit v1.2.3